In object-oriented programming, when a subclass inherits from a superclass, the instantiation of the subclass implies that all the superclass' data is available for use by the subclass. When you instantiate objects of a subclass from database data, all database tables that contain the data held in each class (whether subclass or superclass) must be accessed so that the data can be retrieved and put in the appropriate enterprise objects.
Even in the simplest scenario in which there is a one-to-one mapping between a single database table and an enterprise object, the database and the enterprise objects instantiated from its data have no knowledge of each other. Their mapping is determined by an EOModel. Likewise, inheritance relationships between enterprise objects and the mapping of those relationships onto a database are also managed by an EOModel.
Note: Enterprise Objects Framework doesn't support mapping inheritance hierarchies across tables in separate databases. Instead, you can set up groups of objects connected by cross-database relationships, where related objects forward messages to each other.
Based on these data requirements, you might design a class hierarchy that has a Person class, and Employee and Customer subclasses. As subclasses of Person, Employee and Customer inherit Person's attributes (name and address), but they also implement attributes and behaviors that are specific to their classes.
In addition to designing your class hierarchy, you need to decide how to structure your database so that when objects of the classes are instantiated, the appropriate data is retrieved. Some of the issues you need to weigh in deciding on an approach are:
When a class hierarchy is mapped onto a relational database, data is accessed in two different ways: By fetching just the leaves (for example, just Employee or Customer), and by fetching at the root (Person) to get instances of all levels of the class hierarchy (Employees and Customers).
While deep class hierarchies can be a useful technique in object-oriented programming, you should try to avoid them for enterprise objects. When you attempt to map a deep class hierarchy onto a relational database, the result is likely to be poor performance and a database that's difficult to maintain.
Enterprise Objects Framework supports the three primary approaches for mapping inheritance hierarchies to database tables:
These approaches, along with the advantages and disadvantages of each, are discussed in the following sections. None of them represents a perfect solution-which one is appropriate depends on the needs of your application.Figure 28. Vertical Inheritance Mapping
This method of storage directly reflects the class hierarchy. If an object of the Employee class is retrieved, data for the Employee's Person attributes must be fetched along with Employee data. The relationship between Employee and Person is resolved through a join to give Employee access to its Person data. This is also true for Customer.
Figure 29. Horizontal Inheritance Mapping
This technique entails the same fetching pattern as vertical mapping, except that no joins are performed.
This approach works well for deep class hierarchies, as long as the fetch occurs against the leaves of the class hierarchy (Employee and Customer) rather than against the root (Person). In the case of a deep fetch, it's more efficient than vertical mapping (since no joins are performed). It's the most efficient approach, if you only fetch instances of one leaf subclass at a time.
Figure 30. Single Table Mapping
Each sub-entity maps to the same table and contains attributes only for the properties that are relevant for that class.
Also, if you have a lot of data, this approach can actually be less efficient than horizontal mapping since with single table mapping you have to search the entire table to find the rows needed to instantiate objects of a particular type. (Horizontal mapping is only more efficient if your application just fetches one type of leaf object at a time (instances of a particular subclass).
Fetches from Leaves | Fetches from Root | |
Vertical Mapping | 1 fetch using join | n fetches using join |
Horizontal Mapping | 1 fetch | n fetches |
Single Table Mapping | 1 fetch | 1 fetch |
In the table, "n" represents the number of entities involved in a deep fetch. For example, when you perform a deep fetch against Person in the Person, Customer, Employee class hierarchy, n equals 3.
You can control deep versus shallow fetches by using EOFetchSpecification's setIsDeep method (setIsDeep: in Objective-C). This method specifies whether a fetch should include sub-entities of the fetch specification's entity. If this method is set to true (YES in Objective-C), sub-entities are also fetched; if it's set to false (NO), they aren't. EOFetchSpecifications are deep by default.
When multiple entities are mapped to a single database table, you must set a qualifier on each entity to distinguish its rows from the rows of other entities. You can either do this programmatically by using EOEntity method setRestrictingQualifier (setRestrictingQualifier: in Objective-C), or you can directly specify the qualifier in the EOModeler Advanced Entity Inspector. A restricting qualifier maps an entity to a subset of rows in a table. If you're using single table inheritance mapping, you must use a restricting qualifier that identifies objects of the desired type.
This method (entity:relationshipForRow:relationship: in Objective-C) is invoked when relationships are instantiated for a newly fetched object. The delegate can use the information in the row to determine which entity the target enterprise object should be associated with, and replace the relationship appropriately.
This method (subEntityForEntity:primaryKey:isFinal: in Objective-C) allows the delegate to fine-tune inheritance by indicating from which sub-entity an object should be fetched based on its primary key. The entity returned must be a sub-entity of the specified entity.
In Objective-C, if the delegate knows that the object should be fetched from the returned entity and not one of its sub-entities, it should set the isFinal argument (a pointer to a BOOL) to YES. (In Java, the object must be fetched from the returned entity; it must be final.)
This method (entity:classForObjectWithGlobalID: in Objective-C) is also used to fine-tune inheritance. The delegate can use the specified globalID to determine a subclass to be used in place of the one specified in the entity argument.
Similarly, you can have to-one relationships to instances of leaf subclasses. For example, the PurchaseOrder entity can have a to-one relationship to the Customer entity.
However, in Java you can not have a to-one relationship to a non-leaf entity, an ambiguous to-one relationship, unless you implement a workaround. Ambiguous to-one relationships are not possible in Java because of strong typing in the language. In Objective-C, the class of an instance can be changed after it is instantiated (i.e., from the parent class to the leaf class).
There are two workarounds for Java programmers. You can encode class information in your primary and foreign keys, and implement EOModelGroup's delegate methods as described in "Delegation Hooks for Optimizing Inheritance". If you choose this option, then consider using single table mapping since the table would already contain class information.
The second workaround is to implement the ambiguous to-one relationship as a to-many, similar to dealing with an optional to-one relationship above. "Use a To-Many Relationship"
Table of Contents
Next Section